
#include "FilePackage.h"
#include "mstream.h"
#include <Windows.h>

bool CFilePackage::initHashTable( int iSize )
{
	if(m_pHashTable != NULL)
	{
		delete [] m_pHashTable; 
		m_pHashTable = NULL;
	}
	m_pHashTable = new SHashTableItem [iSize];
	m_sHeader.m_nHashTableSize = iSize;
	return true;
}

bool CFilePackage::addSectItem( const char * name )
{
	if( name )
	{
		struct   stat   _file_stats;   
		if(stat(name,   &_file_stats))   
			return   false;   
		
		if( 0 == _file_stats.st_size )
			return false;

		
		CFileSect *pSect = new CFileSect;
		pSect->setOwner( this );
		pSect->setFileName( name );
		pSect->setDataLen( _file_stats.st_size );
		
		m_listSect.push_back( pSect );
		m_sHeader.m_nSectSize = (int)m_listSect.size();
		return true;
	}

	return false;

}

char * CFilePackage::getSectData( const char * name, int & _dataLen )
{
	//int _tickcout = GetTickCount();
	int _hashIndex = __findHashIndex( name );
	//int _tickcout1 = GetTickCount();

	//int _sub = _tickcout1 - _tickcout;

	int _sectIndex = m_pHashTable[_hashIndex].m_iSectIndex;
	_dataLen       = m_listSect[_sectIndex]->getDataLen();

	//int _tickcout2 = GetTickCount();

	char *buf = new char[_dataLen];
	//int _tickcout3 = GetTickCount();
	//int _sub1 = _tickcout3 - _tickcout2;

	m_listSect[_sectIndex]->readData( buf );

	//int _tickcout4 = GetTickCount();

	//int _sub2 = _tickcout4 - _tickcout3;


	return buf;
}


void CFilePackage::close()
{
	m_fileReader.close();
	m_fileWriter.close();
}

bool CFilePackage::open( const char * fileName )
{
	if( !fileName )
		return false;
	m_fileReader.open( fileName, ios::binary );
	
	

	if( __readHeader(  ) )
	{
		__readHashTable(  );
		__readSectInfo(  );

		//m_fileReader.close();
		return true;
	}
	//m_fileReader.close();
	return false;

}


bool CFilePackage::save( const char * fileName )
{
	if( !fileName )
		return false;

	__fitHashTable();
	__calHashTable();
	unsigned long __dataPos = __calSectsOffset();

	m_fileWriter.open(fileName,ios::binary);
	__writeHeader( );
	__writeHashTable( );
	__writeSectInfo( );

	unsigned long __curPos = m_fileWriter.tellp();
	__writeData();

	m_fileWriter.close();
	return true;

}

bool CFilePackage::__writeData()
{
	for( size_t i=0; i<m_listSect.size(); i++)
	{
		m_listSect[i]->writeData();
	}
	return true;
}


bool CFilePackage::save_2_file( const char *name, const char * fileName )
{
	int _dataLen = 0;
	char *buf = getSectData( name, _dataLen );

	FILE *stream;
	if( (stream = fopen( fileName, "wb+" )) != NULL )
	{
		int numwritten = (int)fwrite( buf, sizeof( char ), _dataLen, stream );
		fclose( stream );
		
		delete[] buf;

		return true;

	}
	return false;

}


bool CFilePackage::__resizeHashTable( int _hashTableSize )
{
	initHashTable( _hashTableSize );
	return true;

}


int  CFilePackage::__findHashIndex( const char * name )
{
	SFileHashValue hashValue;
	m_hashCommon.hashFileName( name, hashValue);

	//calculate the item index in the hash table;
	int hashTableIndex = hashValue.m_iHashIndex % m_sHeader.m_nHashTableSize;
	for( int k = 0 ; k < m_sHeader.m_nHashTableSize ; ++k)
	{
		int index = (hashTableIndex + k) % m_sHeader.m_nHashTableSize;
		if(m_pHashTable[index].m_iFlag != HASHFLAG_NOT_USED )
		{
			if(m_pHashTable[index].m_iHashValue  == hashValue.m_iHashIndex &&
				m_pHashTable[index].m_iName1 == hashValue.m_iName1 && 
				m_pHashTable[index].m_iName2 == hashValue.m_iName2 )
			{
				return index;
			}
		}
	} 
	return -1;
}

bool CFilePackage::__readHeader(  )
{
	m_fileReader.seekg(0);

	m_fileReader.read((char*)&m_sHeader,sizeof(SPackageHeader));


	if( m_sHeader.m_keyChar[0] !='Y' ||
		m_sHeader.m_keyChar[1] !='Y' ||
		m_sHeader.m_keyChar[2] !='Y' ||
		m_sHeader.m_keyChar[3] !='M') 
	{
		return false;
	}
	
	m_listSect.resize( m_sHeader.m_nSectSize );

	initHashTable( m_sHeader.m_nHashTableSize );
	return true;
}


bool CFilePackage::__readHashTable(  )
{
	for (int i=0; i<m_sHeader.m_nHashTableSize; i++)
	{
		m_fileReader.read( (char*)&m_pHashTable[i], sizeof(SHashTableItem));
	}
	return true;

}


bool CFilePackage::__readSectInfo(  )
{
	for( size_t i=0; i<m_listSect.size(); i++)
	{
		m_listSect[i] = new CFileSect;
		m_listSect[i]->setOwner(this);
		m_listSect[i]->readInfo();
	}
	return true;

}

bool CFilePackage::__writeHeader(  )
{
	m_sHeader.m_keyChar[0] = 'Y';
	m_sHeader.m_keyChar[1] = 'Y';
	m_sHeader.m_keyChar[2] = 'Y';
	m_sHeader.m_keyChar[3] = 'M';
	m_sHeader.m_nVersion   = 1000;

	m_fileWriter.write( (char*)&m_sHeader , sizeof(SPackageHeader));

	return true;

}


bool CFilePackage::__writeHashTable(  )
{

	for (int i=0; i<m_sHeader.m_nHashTableSize; i++)
	{
		m_fileWriter.write( (char*)&m_pHashTable[i], sizeof(SHashTableItem));
	}
	return true;

}


bool CFilePackage::__writeSectInfo(  )
{
	for( size_t i=0; i<m_listSect.size(); i++ )
	{
		m_listSect[i]->writeInfo();
	}
	return true;
}

int CFilePackage::_writeData( unsigned long _iOffset, unsigned long _iLen, char *buf )
{
	m_fileWriter.seekp( _iOffset );
	m_fileWriter.write( buf, _iLen );
	return 0;
}


int CFilePackage::_readData( unsigned long _iOffset, unsigned long _iLen, char *buf )
{
	m_fileReader.seekg( _iOffset );
	m_fileReader.read( buf, _iLen );
	return 0;
}

unsigned long  CFilePackage::__calSectsOffset()
{
	m_sHeader.m_nSectSize = (int)m_listSect.size();
	unsigned long _headSize      = sizeof(SPackageHeader);
	unsigned long _hashTableSize = sizeof(SHashTableItem) * m_sHeader.m_nHashTableSize;
	unsigned long _sectInfoSize  = sizeof(SFileSectInfo) * m_sHeader.m_nSectSize;

	unsigned long _firstPos = _headSize + _hashTableSize + _sectInfoSize;

	unsigned long _tempPos = _firstPos;
	for( size_t i=0; i<m_listSect.size(); i++ )
	{
		m_listSect[i]->setOffset( _tempPos );
		_tempPos += m_listSect[i]->getDataLen();
	}

	return _firstPos;
}

void CFilePackage::__calHashTable()
{
	SFileHashValue hashValue;
	size_t _sectNums = m_listSect.size();
	for(size_t i = 0 ; i < _sectNums ; ++ i)
	{
		const char* name = m_listSect[i]->getFileName();
		m_hashCommon.hashFileName( name, hashValue );
		//calculate the item index in the hash table;
		int hashTableIndex = hashValue.m_iHashIndex % m_sHeader.m_nHashTableSize;
		for( int k = 0 ; k < m_sHeader.m_nHashTableSize ; ++k)
		{
			int index = (hashTableIndex + k) % m_sHeader.m_nHashTableSize;
			if(m_pHashTable[index].m_iFlag == HASHFLAG_NOT_USED )
			{
				m_pHashTable[index].m_iHashValue    = hashValue.m_iHashIndex ;
				m_pHashTable[index].m_iName1        = hashValue.m_iName1;
				m_pHashTable[index].m_iName2        = hashValue.m_iName2;
				m_pHashTable[index].m_iSectIndex    = (int)i;
				m_pHashTable[index].m_iFlag       = HASHFLAG_COMMON;
				m_listSect[i]->setHashIndex( index );
				break;
			}
		}   
	}
}

void CFilePackage::__fitHashTable()
{
	if( m_sHeader.m_nHashTableSize < (int)m_listSect.size() * 2 )
	{
		__resizeHashTable( m_sHeader.m_nHashTableSize * 2);
	}
}


void CFilePackage::__write( const char *_Str, streamsize _count )
{
	m_fileWriter.write( _Str , _count);

}

void CFilePackage::__read( char *_Str, streamsize _count )
{
	m_fileReader.read( _Str, _count );

}